/////////////////////////////////////////////////////////////////////////////////
//
//  QAFDebug critical error log
//
//  Copyright (c) 2000-2004
//  Andrew Schetinin
//
//  This software is provided "as is" without express or implied warranty,
//  and with no claim as to its suitability for any purpose.
//
//  Permission to use or copy this software for any purpose is hereby granted
//  without fee, provided the above notices are retained on all copies.
//  Permission to modify the code and to distribute modified code is granted,
//  provided the above notices are retained, and a notice that the code was
//  modified is included with the above copyright notice.
//
//  This software accompanies the article "Code that debugs itself"
//  located at http://www.codeproject.com/debug/qafdebug.asp
//
//  You are welcomed to report bugs, send comments and post code modifications
//  to aschetinin@hotmail.com
//
/////////////////////////////////////////////////////////////////////////////////

///
/// @file	qafdebug.h "../include/qafdebug.h"
/// @brief	Macros for reporting critical errors.
///
///			This file defines a set of macros that replaces some of
///			standard ATL and C++ macros. It is strongly recommended to use
///			these macros in all functions. <P>
///
///			The general guideline of using these macros: if you write code
///			that should never fail under normal conditions, use these macros to report
///			the cases when something extra-ordinary happens. <P>
///
///			For the moment these macros are defined in both RELEASE and DEBUG builds.
///			They use OutputDebugString() and log file to report about errors. <P>
///
///			You must add qafdebug.cpp to your project in order to use this header file.
///

#ifndef _QAFDEBUG_H_
#define _QAFDEBUG_H_

#if _MSC_VER > 1000
	#pragma once
#endif // _MSC_VER > 1000

///
/// @def QAF_DISABLED
/// @brief Disable all error log staff, you may define it for release builds if you want.
///
/// I ENABLE reporting even in RELEASE builds, it makes the program a bit larger (not affecting performance)
/// but instead I get the full diagnostics for all problems. To disable the error reporting in release builds
/// either uncomment these lines or define the QAF_DISABLED conditional define for the release builds.
///
#ifndef QAF_DISABLED
	#define QAF_DISABLED
	// If you want to disable the error log in release builds, uncomment this test of DEBUG build
	//#if (!defined(DEBUG) && !defined(_DEBUG)) || defined(NDEBUG)
		#undef QAF_DISABLED
	//#endif
#endif // QAF_DISABLED

///
/// @def QAF_LOGFILE_DISABLED
/// @brief Disable writing to the error log file, you may define it for release builds if you want.
///
/// I ENABLE writting to the error log file even in RELEASE builds , it makes the program a bit larger
/// (not affecting performance) but instead I get the full diagnostics for all problems.
/// To disable writting to the error log file in release builds either uncomment these lines or define the
/// QAF_LOGFILE_DISABLED conditional define for the release builds.
///
#ifndef QAF_LOGFILE_DISABLED
	#define QAF_LOGFILE_DISABLED
	// If you want to disable the error log in release builds, uncomment this test of DEBUG build
	//#if (!defined(DEBUG) && !defined(_DEBUG)) || defined(NDEBUG)
		#undef QAF_LOGFILE_DISABLED
	//#endif
#endif // QAF_LOGFILE_DISABLED

///
/// @def QAF_OUTPUTDEBUGSTRING_DISABLED
/// @brief Disable calling OutputDebugString(), I recommend you to define it for release builds.
///
/// I DISABLE reporting using OutputDebugString() in RELEASE builds (since usually nobody will trace it).
///
#ifndef QAF_OUTPUTDEBUGSTRING_DISABLED
	#define QAF_OUTPUTDEBUGSTRING_DISABLED
	// This will enable the reporting back for DEBUG build
	#if (defined(DEBUG) || defined(_DEBUG)) && !defined(NDEBUG)
		#undef QAF_OUTPUTDEBUGSTRING_DISABLED
	#endif
#endif // QAF_OUTPUTDEBUGSTRING_DISABLED

/// @def QAF_UNITTEST_DISABLED
/// @brief Disable unit tests related staff, I recommend you to define it for release builds
/// and disable unit tests too.
///
/// I DISABLE part of the functionality related to the unit tests in RELEASE build to optimize the code.
/// Defining this directive removes all the synchronization and memory mapped file staff from the code.
///
#if !defined(WIN32) || defined(NDEBUG)
	#define QAF_UNITTEST_DISABLED
#endif

/// @def QAF_SAVE_UNICODE_AS_UTF8
/// @brief Saves UNICODE error messages to the log file in UTF8 charset.
///
/// When defined, it makes UNICODE error messages to be saved to the log file in UTF8 charset.
/// It improves readability of the error log file when working with both ASCII and UNICODE modules.
/// Minus is that encoding requires to allocate an additional buffer.
///
#if defined(WIN32) && defined(_UNICODE)
	#define QAF_SAVE_UNICODE_AS_UTF8
#endif

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// Includes
///////////////////////////////////////////////////////////////////////////////////////////////////////////

// Common code
#include <stdio.h>

// Mostly Linux-specific code, but GNU compiler for Windows also may use this header
#ifdef HAVE_CONFIG_H
	#include <config.h>
#endif

#ifdef WIN32 // Windows-specific code

	#include <windows.h>
	#include <winerror.h>
	#include <tchar.h>

	typedef TCHAR   Q_TCHAR;
	typedef LPTSTR  Q_LPTSTR;
	typedef LPCTSTR Q_LPCTSTR;
	typedef LPSTR   Q_LPSTR;
	typedef LPCSTR  Q_LPCSTR;
	typedef LPWSTR  Q_LPWSTR;
	typedef LPCWSTR Q_LPCWSTR;

	const unsigned long Q_MAX_PATH = MAX_PATH;

	#if _MSC_VER < 1400

		#ifndef _tfopen_s
			#define _tfopen_s( pFile, filename, mode ) \
				((*(pFile) = _tfopen( (filename), (mode) )) != NULL ? 0 : 1)
		#endif

		#ifndef _tcscpy_s
			#define _tcscpy_s( strDestination, sizeInChars, strSource ) \
				_tcscpy( (strDestination), (strSource) )
		#endif

		#ifndef _tcscat_s
			#define _tcscat_s( strDestination, sizeInChars, strSource ) \
				_tcscat( (strDestination), (strSource) )
		#endif

		#ifndef _taccess_s
			#define _taccess_s( path, mode ) \
				_taccess( (path), (mode) )
		#endif

	#endif

#else // Linux-specific code

	#include <dirent.h>

	typedef char              Q_TCHAR;
	typedef char *            Q_LPTSTR;
	typedef const char *      Q_LPCTSTR;
	typedef char *            Q_LPSTR;
	typedef const char *      Q_LPCSTR;
	typedef wchar_t *        Q_LPWSTR;
	typedef const wchar_t *  Q_LPCWSTR;

	typedef void * HANDLE;
	typedef void * HMODULE;

	#define _T( exp ) exp
	#define _stprintf sprintf
	#define _sntprintf snprintf
	#define _tcsrchr strrchr
	#define _tcscpy strcpy
	#define _tcsncpy strncpy
	#define _tcscat strcat
	#define _tcslen strlen
	#define _tcsncat strncat
	#define _tcschr strchr
	#define _tfopen_s( pFile, filename, mode ) \
		((*(pFile) = fopen( (filename), (mode) )) != NULL ? 0 : 1)
	#define _tcscpy_s( strDestination, sizeInChars, strSource ) \
		strcpy( (strDestination), (strSource) )
	#define _tcscat_s( strDestination, sizeInChars, strSource ) \
		strcat( (strDestination), (strSource) )
	#define _taccess_s( path, mode ) \
		access( (path), (mode) )

	const unsigned long Q_MAX_PATH = PATH_MAX;

#endif

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// Macros
///////////////////////////////////////////////////////////////////////////////////////////////////////////

///
/// @def    Q_ASSERT
/// @brief  This macro reports about critical error if @c bCondition evaluates to @c false.
/// @param  bCondition	Any expression that evaluates to @c bool or @c long
/// @return bool (same as bCondition)
/// @author Andrew Schetinin
/// @date   September 26, 2002
///
///         This macro output a error report string to the error log (file and/or debug console).
///         The string looks like that:
/// @code
/// c:\SharedUnits\Dev\QMSOColl\QMSOColl.cpp(56) : Assertion raised
/// 	time:        2003-12-08 16:11:34:003
/// 	process:     0x000006F4
/// 	thread:      0x00000148
/// 	application: c:\ErrorLog_demo\Debug\ErrorLog.exe <1.0.0.3>
/// 	module:      c:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.0.0.10>
/// 	last error:  2, The system cannot find the file specified.
/// 	expression:  false
/// @endcode
///         It is recommended to use this macro in all places where any error
///         means program crash or non-predictable behavior of dependent code.
///         It will help to detect where first the error appeared.
///         Recommended for IFs without ELSE or testing return values of functions.
/// @code
///	if( Q_ASSERT( TRUE == bVarShouldAlwaysBeTRUE ) ) // here it will write to the error log
///		do_something();
/// @endcode
///

#ifndef QAF_DISABLED
	#define Q_ASSERT( bCondition ) ((bCondition) ? true : (QAFDebug::OutputDebugStringEx( \
		_T(__FILE__), __LINE__, QAF_EXPR(_T(#bCondition)), QAFDEBUG_ERROR_ASSERTION ), false) )
#else
	#define Q_ASSERT( bCondition ) (bCondition)
#endif

///
/// @def    Q_CHECK
/// @brief  This macro reports about critical error if @c exprConst != @c exprCheck.
/// @param  exprConst	Any expression that evaluates to @c bool or @c long
/// @param  exprCheck	Any expression that evaluates to @c bool or @c long
/// @return bool (same as (exprConst == exprCheck))
/// @author Andrew Schetinin
/// @date   April 10, 2003
///
///         This macro output a error report string to the error log (file and/or debug console).
///         The important thing about this macro is that it outputs
///         also the actual compared values (error codes, handles, etc.)
///         The string looks like that:
/// @code
/// c:\SharedUnits\Dev\QMSOColl\QMSOColl.cpp(56) : Check error: got 10 (0xA) while expected 0 (0x0)
/// 	time:        2003-12-08 16:11:34:003
/// 	process:     0x000006F4
/// 	thread:      0x00000148
/// 	application: c:\ErrorLog_demo\Debug\ErrorLog.exe <1.0.0.3>
/// 	module:      c:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.0.0.10>
/// 	last error:  2, The system cannot find the file specified.
/// 	expression:  ERROR_SUCCESS == ret
/// @endcode
///         It is recommended to use this macro in all places where any error
///         means program crash or non-predictable behavior of dependent code.
///         It will help to detect where first the error appeared.
///         Recommended for IFs without ELSE or testing return values of functions.
/// @code
///	if( Q_CHECK( ERROR_SUCCESS, ret ) ) // here it will write to the error log
///		do_something();
/// @endcode
///

#ifndef QAF_DISABLED
	#define Q_CHECK( exprConst, exprCheck ) QAFDebug::ReportCheckError( (exprConst), \
		(exprCheck), QAF_EXPR( _T(#exprConst) _T(" == ") _T(#exprCheck) ), _T(__FILE__), __LINE__ )
#else
	#define Q_CHECK( exprConst, exprCheck ) ((exprConst) == (exprCheck))
#endif

///
/// @def    Q_INVALID
/// @brief  This macro reports about critical error if @c bCondition evaluates to @c true.
/// @param  bCondition	Any expression that evaluates to @c bool or @c long
/// @return bool (same as bCondition)
/// @author Andrew Schetinin
/// @date   December 11, 2002
///
///         This macro output a error report string to the error log (file and/or debug console).
///         The string looks like that:
/// @code
/// c:\SharedUnits\Dev\QMSOColl\QMSOColl.cpp(56) : Assumption failed
/// 	time:        2003-12-08 16:11:34:003
/// 	process:     0x000006F4
/// 	thread:      0x00000148
/// 	application: c:\ErrorLog_demo\Debug\ErrorLog.exe <1.0.0.3>
/// 	module:      c:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.0.0.10>
/// 	last error:  2, The system cannot find the file specified.
/// 	expression:  NULL == lpszStringParam
/// @endcode
///         It is recommended to use this macro in all places where any error
///         means program crash or non-predictable behavior of dependent code.
///         It will help to detect where first the error appeared.
///         Recommended for testing input parameters or error conditions.
/// @code
///	if( Q_INVALID( NULL == lpszStringParam ) ) // here it will write to the error log
///		return E_INVALIDARG;
/// @endcode
///

#ifndef QAF_DISABLED
	#define Q_INVALID( bCondition ) ((bCondition) ? (QAFDebug::OutputDebugStringEx( \
		_T(__FILE__), __LINE__, QAF_EXPR(_T(#bCondition)), QAFDEBUG_ERROR_INVALID_ASSUMPTION ), true) : false )
#else
	#define Q_INVALID( bCondition ) (bCondition)
#endif

///
/// @def    Q_SUCCEEDED
/// @brief  Add reporting critical errors to the standard SUCCEEDED macro.
/// @param  Status	HRESULT result code
/// @return bool. @c true means "no error"
/// @author Andrew Schetinin
/// @date   September 26, 2002
///
///         Generic test for success on any status value (non-negative numbers
///         indicate success). <P>
///
///         This macro output a error report string to the error log (file and/or debug console).
///         The string looks like that:
/// @code
/// c:\SharedUnits\Dev\QMSOColl\QMSOColl.cpp(56) : The component's CLSID is missing or corrupt.
/// 	time:        2003-12-08 16:11:34:003
/// 	process:     0x000006F4
/// 	thread:      0x00000148
/// 	application: c:\ErrorLog_demo\Debug\ErrorLog.exe <1.0.0.3>
/// 	module:      c:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.0.0.10>
/// 	last error:  0
/// 	expression:  Q_FAILED(0x800401F3)
/// @endcode
///         It is recommended to use this macro in all places where any error
///         means program crash or non-predictable behavior of dependent code.
///         It will help to detect where first the error appeared.
///         Recommended for testing return HRESULTs that usually always should return S_OK or S_FALSE.
///         Another use for this macro is to report about a critical error in your function
///         that is returned to the calling function.
/// @code
///	if( Q_SUCCEEDED(hr) ) // here it will write to the error log
///		do_something();
/// @endcode
///

#ifndef QAF_DISABLED
	#define Q_SUCCEEDED(hResultExpr) SUCCEEDED(Q_ERROR(hResultExpr))
#else
	#define Q_SUCCEEDED(hResultExpr) SUCCEEDED(hResultExpr)
#endif

///
/// @def    Q_FAILED
/// @brief  Add reporting critical errors to the standard FAILED macro.
/// @param  Status	HRESULT result code
/// @return bool. @c true means "there is an error!"
/// @author Andrew Schetinin
/// @date   September 26, 2002
///
///         Generic test for failure on any status value. <P>
///
///         This macro output a error report string to the error log (file and/or debug console).
///         The string looks like that:
/// @code
/// c:\SharedUnits\Dev\QMSOColl\QMSOColl.cpp(56) : The component's CLSID is missing or corrupt.
/// 	time:        2003-12-08 16:11:34:003
/// 	process:     0x000006F4
/// 	thread:      0x00000148
/// 	application: c:\ErrorLog_demo\Debug\ErrorLog.exe <1.0.0.3>
/// 	module:      c:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.0.0.10>
/// 	last error:  0
/// 	expression:  Q_FAILED(0x800401F3)
/// @endcode
///         It is recommended to use this macro in all places where any error
///         means program crash or non-predictable behavior of dependent code.
///         It will help to detect where first the error appeared.
///         Recommended for testing return HRESULTs that usually always should return S_OK or S_FALSE.
///         Another use for this macro is to report about the error before returning from your function.
/// @code
///	if( Q_FAILED(hr) ) // here it will write to the error log
///		return hr;
/// @endcode
///

#ifndef QAF_DISABLED
	#define Q_FAILED(hResultExpr) FAILED(Q_ERROR(hResultExpr))
#else
	#define Q_FAILED(hResultExpr) FAILED(hResultExpr)
#endif

///
/// @def    Q_ERROR
/// @brief  Reports critical errors returned from your function before returning.
/// @param  Status	HRESULT result code
/// @return HRESULT (same as Status)
/// @author Andrew Schetinin
/// @date   September 26, 2002
///
///         Generic test for failure on any status value. <P>
///
///         This macro output a error report string to the error log (file and/or debug console).
///         The string looks like that:
/// @code
/// c:\SharedUnits\Dev\QMSOColl\QMSOColl.cpp(56) : The component's CLSID is missing or corrupt.
/// 	time:        2003-12-08 16:11:34:003
/// 	process:     0x000006F4
/// 	thread:      0x00000148
/// 	application: c:\ErrorLog_demo\Debug\ErrorLog.exe <1.0.0.3>
/// 	module:      c:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.0.0.10>
/// 	last error:  0
/// 	expression:  Q_FAILED(0x800401F3)
/// @endcode
///         It is recommended to use this macro in all places where any error
///         means program crash or non-predictable behavior of dependent code.
///         It will help to detect where first the error appeared.
///         Recommended for testing return HRESULTs that usually always should return S_OK or S_FALSE.
///         Another use for this macro is to test return values of generic functions that
///         usually never fail.
/// @code
///	// This will report about the error in the exact place where it first happened.
///	return Q_ERROR( CoCreateInstance( clsid, NULL, dwCtx, IID_IDispatch, (void**)(&this->p) ) );
/// @endcode
///

#ifndef QAF_DISABLED
	#define Q_ERROR(hResultExpr) (QAFDebug::ReportComError( hResultExpr, _T(__FILE__), __LINE__ ))
#else
	#define Q_ERROR(hResultExpr) (hResultExpr)
#endif

///
/// @def    Q_ERROR_SUCCESS
/// @brief  Reports critical errors from ERROR_SUCCESS group.
/// @param  dwError	Error result code.
/// @return bool (dwError == ERROR_SUCCESS)
/// @author Andrew Schetinin
/// @date   March 19, 2003
///
///         Generic test for failure on error code. <P>
///
///         This macro output a error report string to the error log (file and/or debug console).
///         The string looks like that:
/// @code
/// c:\SharedUnits\Dev\QMSOColl\QMSOColl.cpp(56) : More data is available.
/// 	time:        2003-12-08 16:11:34:003
/// 	process:     0x000006F4
/// 	thread:      0x00000148
/// 	application: c:\ErrorLog_demo\Debug\ErrorLog.exe <1.0.0.3>
/// 	module:      c:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.0.0.10>
/// 	last error:  123, More data is available.
/// 	expression:  ERROR_SUCCESS != 123
/// @endcode
///         It is recommended to use this macro in all places where any error
///         means program crash or non-predictable behavior of dependent code.
///         It will help to detect where first the error appeared.
///         Recommended for testing return error codes that usually always should return ERROR_SUCCESS.
///         Another use for this macro is to test return values of generic functions that
///         usually never fail.
/// @code
/// 	// This will report about the error in the exact place where it first happened.
/// 	unsigned long dwRes = RegQueryValueEx( hKey, _T("Value"), NULL, &dwType, (LPBYTE)szBuf, &dwSize );
/// 	if( Q_ERROR_SUCCESS(dwRes) )
/// 		/*do something*/;
/// @endcode
///

#ifndef QAF_DISABLED
#define Q_ERROR_SUCCESS(dwErrorExpr) (ERROR_SUCCESS == QAFDebug::ReportWinError( dwErrorExpr, _T(__FILE__), __LINE__ ))
#else
#define Q_ERROR_SUCCESS(dwErrorExpr) (ERROR_SUCCESS == (dwErrorExpr))
#endif

///
/// @def    Q_LOG
/// @brief  Reports critical errors with a string message.
/// @param  lpszMessage	Q_LPCTSTR message string
/// @return void
/// @author Andrew Schetinin
/// @date   January 28, 2003
///
///         This is a macro for reporting about critical errors in a user-understandable format.
///         Generally it is preferable to Q_ASSERT(false). <P>
///
///         This macro output a error report string to the error log (file and/or debug console).
///         The string looks like that:
/// @code
/// c:\SharedUnits\Dev\QMSOColl\QMSOColl.cpp(56) : My custom error message.
/// 	time:        2003-12-08 16:11:34:003
/// 	process:     0x000006F4
/// 	thread:      0x00000148
/// 	application: c:\ErrorLog_demo\Debug\ErrorLog.exe <1.0.0.3>
/// 	module:      c:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.0.0.10>
/// 	last error:  0
/// 	expression:  Error message
/// @endcode
///         It is recommended to use this macro in all places where any error
///         means program crash or non-predictable behavior of dependent code.
///         It will help to detect where first the error appeared.
///         Recommended for IFs without ELSE or testing return values of functions.
/// @code
///	catch( ... )
///	{
///		Q_LOG( _T("Unknown exception catched") ); // here it will report about all exceptions
///	}
/// @endcode
///

#ifndef QAF_DISABLED
	#define Q_LOG(lpszMessage) (QAFDebug::OutputDebugStringEx( _T(__FILE__), __LINE__, QAFDEBUG_ERROR_LOG, lpszMessage ))
#else
	#define Q_LOG(lpszMessage) (lpszMessage)
#endif

///
/// @def    Q_MFC_EXCEPTION
/// @brief  Reports critical exceptions.
/// @param  e	CException object instance (MFC-style)
/// @return void
/// @author Andrew Schetinin
/// @date   November 18, 2002
///
///         This is a special macro to report about an MFC-style exception. <P>
///
///         This macro output a error report string to the error log (file and/or debug console).
///         The string looks like that:
/// @code
/// c:\SharedUnits\Dev\QMSOColl\QMSOColl.cpp(56) : Cannot open file
/// 	time:        2003-12-08 16:11:34:003
/// 	process:     0x000006F4
/// 	thread:      0x00000148
/// 	application: c:\ErrorLog_demo\Debug\ErrorLog.exe <1.0.0.3>
/// 	module:      c:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.0.0.10>
/// 	last error:  0
/// 	expression:  e
/// @endcode
///         It is recommended to use this macro in all places where any error
///         means program crash or non-predictable behavior of dependent code.
///         It will help to detect where first the error appeared.
///         Recommended for reporting about exceptions in CATCH clauses.
///         You may define your own exception classes (without MFC support), just define
///         the GetErrorMessage() method and this macro will work.
/// @code
///	catch( CMyException & e )
///	{
///		Q_MFC_EXCEPTION(e); // here it will write to the error log with the error message
///	}
///	catch( ... )
///	{
///		Q_LOG( _T("Unknown exception catched") ); // here it will report about any other exception
///	}
/// @endcode
///

#ifndef QAF_DISABLED
	#define Q_MFC_EXCEPTION(e) \
	do { \
		Q_TCHAR szBuf[251] = { 0 }; \
		if( (NULL == e) || (! e->GetErrorMessage( szBuf, 250, NULL )) ) \
			strcpy( szBuf, QAFDEBUG_ERROR_NO_MESSAGE ); \
		QAFDebug::OutputDebugStringEx( _T(__FILE__), __LINE__, QAF_EXPR(_T(#e)), szBuf ); \
	} while( false )
#else
	#define Q_MFC_EXCEPTION(e) (e)
#endif

/// Synonim for Q_MFC_EXCEPTION macro - left for backward compatibility.
#define Q_EXCEPTION(e) Q_MFC_EXCEPTION(e)

///
/// @def    Q_STD_EXCEPTION
/// @brief  Reports critical exceptions.
/// @param  e	std::exception object instance (STL-style)
/// @return void
/// @author Andrew Schetinin
/// @date   December 11, 2003
///
///         This is a special macro to report about an STL exception. <P>
///
///         This macro output a error report string to the error log (file and/or debug console).
///         The string looks like that:
/// @code
/// c:\SharedUnits\Dev\QMSOColl\QMSOColl.cpp(56) : Cannot open file
/// 	time:        2003-12-08 16:11:34:003
/// 	process:     0x000006F4
/// 	thread:      0x00000148
/// 	application: c:\ErrorLog_demo\Debug\ErrorLog.exe <1.0.0.3>
/// 	module:      c:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.0.0.10>
/// 	last error:  0
/// 	expression:  e
/// @endcode
///         It is recommended to use this macro in all places where any error
///         means program crash or non-predictable behavior of dependent code.
///         It will help to detect where first the error appeared.
///         Recommended for reporting about exceptions in CATCH clauses.
/// @code
///	catch( std::exception & e )
///	{
///		Q_STD_EXCEPTION(e); // here it will write to the error log with the error message
///	}
///	catch( ... )
///	{
///		Q_LOG( _T("Unknown exception catched") ); // here it will report about any other exception
///	}
/// @endcode
///

#ifndef QAF_DISABLED
	#define Q_STD_EXCEPTION(e) QAFDebug::OutputDebugStringEx( _T(__FILE__), __LINE__, QAF_EXPR(_T(#e)), e.what() )
#else
	#define Q_STD_EXCEPTION(e) (e)
#endif

///
/// @def    Q_MAPI_ERROR
/// @brief  Reports critical exceptions.
/// @param  hresult       (HRESULT) - MAPI error code
/// @param  piObjectMAPI  (any of MAPI interfaces)
/// @return void
/// @author Andrew Schetinin
/// @date   November 18, 2002
///
///         This is a special macro to report about an MAPI error. <P>
///
///         This macro output a error report string to the error log (file and/or debug console).
///         The string looks like that:
/// @code
/// c:\SharedUnits\Dev\QMSOColl\QMSOColl.cpp(56) : Cannot login to MAPI storage
/// 	time:        2003-12-08 16:11:34:003
/// 	process:     0x000006F4
/// 	thread:      0x00000148
/// 	application: c:\ErrorLog_demo\Debug\ErrorLog.exe <1.0.0.3>
/// 	module:      c:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.0.0.10>
/// 	last error:  0
/// 	expression:  hr
/// @endcode
///         It is recommended to use this macro in all places where any error
///         means program crash or non-predictable behavior of dependent code.
///         It will help to detect where first the error appeared.
///         Recommended for reporting about critical MAPI errors.
/// @code
///	Q_MAPI_ERROR( hresult, piObjectMAPI );
/// @endcode
///

#ifndef QAF_DISABLED
	// taken from MAPI Inside, they do not use SUCCEEDED
	#define Q_MAPI_ERROR( hresult, piObjectMAPI ) \
	do { \
		HRESULT hrtemp39472 = hresult; \
		LPMAPIERROR pErr = NULL; \
		if( Q_ASSERT( NULL != piObjectMAPI ) \
			&& Q_ASSERT( S_OK == piObjectMAPI->GetLastError( hrtemp39472, 0, &pErr ) ) \
			&& Q_ASSERT( NULL != pErr ) ) \
		{ \
			long BUF_SIZE = 128 + _tcslen(pErr->lpszError) + _tcslen(pErr->lpszComponent); \
			Q_LPTSTR szBuf = (Q_LPTSTR)malloc( BUF_SIZE * sizeof(Q_TCHAR) ); \
			if( Q_ASSERT( NULL != szBuf ) ) \
			{ \
				_stprintf( szBuf, _T("MAPI Error: %s. Component: %s. Version: %08X. ") \
					_T("LowLevelError: %08X. Context: %08X."), \
					((NULL != pErr->lpszError) ? pErr->lpszError : _T("")), \
					((NULL != pErr->lpszComponent) ? pErr->lpszComponent : _T("")), \
					pErr->ulVersion, pErr->ulLowLevelError, pErr->ulContext ); \
				Q_TCHAR szHRBuf[20] = { 0 }; \
				_stprintf( szHRBuf, _T("Q_FAILED(%08X)"), hrtemp39472 ); \
				QAFDebug::OutputDebugStringEx( _T(__FILE__), __LINE__, szHRBuf, szBuf ); \
				free( szBuf ); \
			} \
		} \
	} while( false )
#else
	#define Q_MAPI_ERROR( hresult, piObjectMAPI ) (hresult)
#endif

///
/// @def	Q_RETURN
/// @brief	If input HRESULT failed, then macro returns it.
/// @param	hr	HRESULT
/// @return	hr, if
/// @author	Andrey Krasnopolsky
/// @date	August 05, 2003
///
///		This macro is for testing HRESULTs. It gets HRESULT as input, tests it and
///		returns the same HRESULT in case of error.
///		Very useful if you need to break function execution in case of error.
/// @code
/// // if the method returns E_XXX, it will exit and return the error code
///	Q_RETURN( CallSomeMethod() );
/// @endcode
///

#define Q_RETURN(hr) do { HRESULT hrtmp83457 = hr; if( Q_SUCCEEDED(hrtmp83457) ) ; else return hrtmp83457; } while( false )

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// Defines related to the unit tests
///////////////////////////////////////////////////////////////////////////////////////////////////////////

///
/// @def	Q_ENABLE_DEBUG_LOG
/// @brief	Enable the error log in case if it was disabled because of the previous unit test failure.
/// @return	void
/// @author	Andrew Schetinin
/// @date	November 18, 2002
///
///		This is a special macro for unit test functions. This macro ensures that the
///		reporting is switched on. It is recommended to put it at the beginning of a single
///		tests case (at the beginning of the function).
/// @code
///	void CUnitTest::testCase01( void )
///	{
///		Q_ENABLE_DEBUG_LOG; // switch the error log on
///		CPPUNIT_ASSERT( Q_SUCCEEDED( QAFGetRegKey( HKCU_C_END, &str ) ) ); // write to the log if it fails
///		...
///	}
/// @endcode
///

#define Q_ENABLE_DEBUG_LOG ;
#ifndef QAF_DISABLED
	#ifndef QAF_UNITTEST_DISABLED
		#undef Q_ENABLE_DEBUG_LOG
		#define Q_ENABLE_DEBUG_LOG QAFDebug::tryEnable();
	#endif
#endif

///
/// @def	Q_SILENT
/// @brief	Temporary disable the error log and evaluate the expression.
/// @param	expr	Any expression
/// @return	void
/// @author	Andrew Schetinin
/// @date	November 18, 2002
///
///		This is a special macro for unit test functions. It is useful for testing the wrong cases
///		(for example, passing wrong parameters and checking that the function fails).
///		For wrong test cases we do not want to report about errors because we want them to happen.
/// @code
///	void CUnitTest::testCase01( void )
///	{
///		Q_ENABLE_DEBUG_LOG; // switch the error log on
///		CPPUNIT_ASSERT( Q_SUCCEEDED( QAFGetRegKey( HKCU_C_END, &str ) ) ); // write to the log if it fails
///		Q_SILENT( CPPUNIT_ASSERT( Q_FAILED( QAFGetRegKey( NULL, NULL ) ) ) ); // do not write to log
///		...
///	}
/// @endcode
///

#define Q_SILENT(expr) expr;
#ifndef QAF_DISABLED
	#ifndef QAF_UNITTEST_DISABLED
		#undef Q_SILENT
		#define Q_SILENT(expr) \
		do { \
			bool bLogDisabled = QAFDebug::tryDisable(); \
			expr; \
			if( bLogDisabled ) \
				QAFDebug::tryEnable(); \
		} while( false )
	#endif
#endif

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// Conditional defines
///////////////////////////////////////////////////////////////////////////////////////////////////////////

///
/// @def Q_SET_MODULE
/// @brief This macro allows setting the module name for additional logging options.
/// @param Q_LPCTSTR, name of the DLL or EXE, 0-terminated, not longer than MAX_PATH characters.
/// @return void
/// @author	Andrew Schetinin
/// @date	December 7, 2003
///
/// This will switch on logging of the file name and version number of the current EXE or DLL.
/// It will be logged after the file name and path of the hosting process,
/// only the module file name without path is logged: FILENAME.DLL(1.0.0.1)
///
#if !defined(QAF_DISABLED)
	#define Q_SET_MODULE( szModuleName ) QAFDebug::SetModule( szModuleName )
#else
	#define Q_SET_MODULE( szModuleName )
#endif

// Only if reporting is not disabled
#ifndef QAF_DISABLED

/// The name of the mutex for synchronizing the unit test support staff.
const Q_LPCTSTR QAFDEBUG_SILENCE_MUTEX = _T("QAFDebugMutex001A");

/// This company name will be used for error log folders and registry settings.
#define QAFDEBUG_COMPANY_NAME "Company"

/// Name of the environment variable that may set the output debug log folder.
/// This is not supported on Linux since there is a standard location for log files.
const Q_LPCTSTR QAFDEBUG_LOG_ENV_VAR = _T("QAFERRORLOGPATH");

/// @brief Maximum log file size
///
/// Maximum log file size (there are two log files - one current and second previous).
/// When the log file size exceeds half of this limit, it is renamed to the second name
/// (thus both files together cannot take more than this maximum size).
/// The size is in bytes. Usually 1 record takes about 500 characters, so I reserve
/// space for about 2,000 records with 1 Mb limit.
const unsigned long QAFDEBUG_LOG_FILE_MAX_SIZE = (1024 * 1024);

/// The current error log file name
const Q_LPCTSTR QAFDEBUG_LOG_FILE_NAME = _T("error.log");

/// The previous error log file name
const Q_LPCTSTR QAFDEBUG_LOG_OLD_FILE_NAME = _T("error.old.log");

/// The name of the memory-mapped-file that stores the shared flags
const Q_LPCTSTR QDEBUG_SHMEMFILE = _T("QAFDbgMemFile01");

/// Errors in the reporting engine
#define QAFDEBUG_ERROR_PREFIX _T("Debug System Error --> ")

/// Fixed error message for assertion raised
#define QAFDEBUG_ERROR_ASSERTION _T("Assertion raised")

/// Fixed error message for invalid assumption raised
#define QAFDEBUG_ERROR_INVALID_ASSUMPTION _T("Invalid assumption is raised")

/// Fixed error message for unknown error
#define QAFDEBUG_ERROR_NO_MESSAGE _T("[Could not find any description for the error]")

/// Fixed expression for the custom error message
#define QAFDEBUG_ERROR_LOG _T("Error Message")

///
/// @def QAF_EXPR
/// @brief The Expression string is printed to the error log only in DEBUG builds.
///
///        It makes the binary a bit smaller for the release build.
///
#if (defined(DEBUG) || defined(_DEBUG)) && !defined(NDEBUG)
	#define QAF_EXPR(expression) (expression)
#else
	#define QAF_EXPR(expression) (NULL)
#endif

#endif

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// class CQAFDebug - this is a service class - do not use it directly!
///////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef QAF_DISABLED

///
/// @namespace QAFDebug
/// @brief Namespace QAFDebug hides the debug functions from global scope.
///
namespace QAFDebug
{
	///
	/// @brief Try to enable the error log
	/// @return bool true if the log is enabled successfully
	/// @author	Andrew Schetinin
	/// @date	November 20, 2002
	///
	/// Try to enable the error log. This function is used only in unit tests.
	///	Do not use it in your code!
	///
	bool tryEnable( void );

	///
	/// @brief Try to disable the error log
	/// @return bool true if the log is disabled successfully
	/// @author	Andrew Schetinin
	/// @date	November 20, 2002
	///
	/// Try to disable the error log. This function is used only in unit tests.
	///	Do not use it in your code!
	///
	bool tryDisable( void );

	///
	/// @brief Reports about critical errors
	/// @param	szFilename	Q_LPCTSTR - the file name returned by _FILE_
	/// @param	iLine		const long - the line number returned by _LINE_
	/// @param	szExpression	Q_LPCTSTR - the expression where the error was detected
	/// @param	szErrorMessage	Q_LPCTSTR - the error message generated
	/// @param	pdwLastError	const unsigned long * - the last error code must be restored
	/// @author	Andrew Schetinin
	/// @date	November 20, 2002
	///
	/// Reports critical errors.
	///	This function is used by debug macroses. Do not use it in your code!
	///
	void OutputDebugStringEx( Q_LPCTSTR szFilename, const long iLine, Q_LPCTSTR szExpression,
		Q_LPCTSTR szErrorMessage, const unsigned long * pdwLastError = NULL );

	///
	/// @brief Reports about critical errors if ulConstExpr != ulCheckExpr
	/// @param	ulConstExpr	unsigned long - the expected expression
	/// @param	ulCheckExpr	unsigned long - the returned expression
	/// @param	szExpr		Q_LPCTSTR - the expression of ulCheckExpr
	/// @param	szFile		Q_LPCTSTR - the file name returned by _FILE_
	/// @param	iLine		const long - the line number returned by _LINE_
	///	@return	bool		the same as (ulConstExpr == ulCheckExpr)
	/// @author	Andrew Schetinin
	/// @date	July 2, 2003
	///
	/// Reports critical errors if ulConstExpr != ulCheckExpr.
	///	This function is used by debug macroses. Do not use it in your code!
	///
	inline bool ReportCheckError( const unsigned long ulConstExpr,
		const unsigned long ulCheckExpr, Q_LPCTSTR szExpr, Q_LPCTSTR szFile,
		const long iLine )
	{
		if( ulConstExpr == ulCheckExpr )
			return true;
		const unsigned short BUF_SIZE = 100;
		Q_TCHAR szErrorMessage[BUF_SIZE] = { 0 };
		#if defined(WIN32) && (_MSC_VER >= 1400)
			_stprintf_s( szErrorMessage, BUF_SIZE,
		#else
			_stprintf( szErrorMessage, 
		#endif
			_T("Check error: got %d (0x%X) while expected %d (0x%X)"),
			ulCheckExpr, ulCheckExpr, ulConstExpr, ulConstExpr );
		OutputDebugStringEx( szFile, iLine, szExpr, szErrorMessage );
		return false;
	}

	///
	/// @brief Return an accessible directory name for all log files.
	/// @param	lpszDirBuf	Q_LPTSTR - buffer for the directory name
	/// @param	dwMaxLen	const unsigned long - size of the buffer in characters (including the trailing zero)
	///	@return	unsigned long - length of the returned string or 0 in case of error
	/// @author	Andrew Schetinin
	/// @date	February 6, 2003
	///
	/// Get the buffer and the buffer length in Q_TCHAR characters including tailing 0x00(00).
	/// Returns the length of the written string or 0 if the directory name cannot be generated.
	/// The directory name is constructed from the: <p>
	/// 1. Try get the folder path from the environment variable QAFDEBUG_LOG_ENV_VAR <p>
	/// 2. Try CSIDL_APPDATA (C:\Documents and Settings\username\Application Data) + QAFDEBUG_LOG_SUBFOLDER <p>
	/// 3. Try CSIDL_COMMON_APPDATA (C:\Documents and Settings\All Users\Application Data)
	///    + QAFDEBUG_LOG_SUBFOLDER <p>
	/// 4. Return 0 <p>
	/// If the folders are missing on the disk, they are created.
	///
	unsigned long GetLogDir( Q_LPTSTR lpszDirBuf, const unsigned long dwMaxLen );

	///
	/// @brief This function allows setting the module name for additional logging options.
	/// @param Q_LPCTSTR, name of the DLL or EXE, 0-terminated, not longer than MAX_PATH characters.
	/// @return void
	/// @author	Andrew Schetinin
	/// @date	December 7, 2003
	///
	/// This will switch on logging of the file name and version number of the current EXE or DLL.
	/// It will be logged after the file name and path of the hosting process,
	/// only the module file name without path is logged: FILENAME.DLL(1.0.0.1)
	///
	void SetModule( Q_LPCTSTR szModuleName );

// Windows-specific functions
#ifdef WIN32

	///
	/// @brief Reports about critical errors if HRESULT is failed
	/// @param	hrStatus	HRESULT - tested on failure
	/// @param	szFile		Q_LPCTSTR - the file name returned by _FILE_
	/// @param	iLine		const long - the line number returned by _LINE_
	///	@return	HRESULT		the same hrStatus that it received
	/// @author	Andrew Schetinin
	/// @date	November 20, 2002
	///
	/// Reports critical errors if HRESULT is failed.
	///	This function is used by debug macroses. Do not use it in your code!
	///
	HRESULT qafReportComError( const HRESULT hrStatus, Q_LPCTSTR szFile, const long iLine );

	///
	/// @brief Reports about critical errors if dwError != ERROR_SUCCESS.
	/// @param	dwError		unsigned long - tested on failure
	/// @param	szFile		Q_LPCTSTR - the file name returned by _FILE_
	/// @param	iLine		const long - the line number returned by _LINE_
	///	@return	unsigned long		the same as dwError
	/// @author	Andrew Schetinin
	/// @date	March 19, 2003
	///
	/// Reports critical errors if dwError != ERROR_SUCCESS.
	///	This function is used by debug macroses. Do not use it in your code!
	///
	unsigned long ReportWinError( const unsigned long dwError, LPCTSTR szFile, const long iLine );

	///
	/// @brief Reports about critical errors if HRESULT is failed
	/// @param	hrStatus	HRESULT - tested on failure
	/// @param	szFile		Q_LPCTSTR - the file name returned by _FILE_
	/// @param	iLine		const long - the line number returned by _LINE_
	///	@return	HRESULT		the same hrStatus that it received
	/// @author	Andrew Schetinin
	/// @date	November 20, 2002
	///
	/// Reports critical errors if HRESULT is failed.
	///	This function is used by debug macroses. Do not use it in your code!
	///
	inline HRESULT ReportComError( const HRESULT hrStatus, Q_LPCTSTR szFile, const long iLine )
	{
		if( FAILED(hrStatus) )
			qafReportComError( hrStatus, szFile, iLine );
		return hrStatus;
	}

// Linux-specific functions
#else

#endif

}

#endif


///////////////////////////////////////////////
// END OF FILE
///////////////////////////////////////////////
#endif

